﻿/**************************************************************
 *
 * 唯一标识：f03febf8-474e-43e7-a054-d9caffc84165
 * 命名空间：Sgr.Data
 * 创建时间：2024/6/17 21:29:18
 * 机器名称：DESKTOP-HJ4OAG9
 * 创建者：CocoYuan
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：默认数据库连接字符串提供者实现
 *
 *  ****************************  Sqlite连接字符串 **************************
 *
 *  "Data Source=/data/sgr/sgr-prod.db;Cache=Shared;Mode=ReadWrite;Foreign Keys=True;Pooling=True;Default Timeout=30;Journal Mode=WAL;Synchronous=Normal;Password=your_strong_password;"
 *
 *  基础配置
 *  •	Data Source=/data/sgr/sgr-prod.db - 数据库文件的绝对路径
 *  •	Mode=ReadWrite - 读写模式
 *  •	Foreign Keys=True - 启用外键约束
 *
 *  性能优化：
 *  •	Cache=Shared - 启用共享缓存模式，提高并发访问性能
 *  •	Journal Mode=WAL - 使用 WAL（Write-Ahead Logging）日志模式，提升写入性能
 *  •	Synchronous=Normal - 在保证数据安全的同时优化性能
 *
 *  安全性：
 *  •	Password=your_strong_password - 数据库文件加密密码
 *  •	Pooling=True - 启用连接池
 *  •	Default Timeout=30 - 命令执行超时时间（秒）
 *
 *  ****************************  MySQL连接字符串 **************************
 *
 *  "Server=localhost;Port=3306;Database=sgr-prod;User Id=sgr_user;Password=your_strong_password;CharSet=utf8mb4;AllowPublicKeyRetrieval=true;SslMode=Required;UseAffectedRows=true;ConnectionTimeout=10;DefaultCommandTimeout=30;Pooling=true;MinimumPoolSize=10;MaximumPoolSize=100;ConnectionLifeTime=300;ConnectionReset=true;"
 *
 *  安全相关：
 *  •	SslMode=Required - 强制使用 SSL/TLS 加密连接
 *  •	AllowPublicKeyRetrieval=true - 允许客户端从服务器获取公钥
 *
 *  性能优化：
 *  •	MinimumPoolSize=10 - 保持最少 10 个连接在池中
 *  •	MaximumPoolSize=100 - 最大允许 100 个连接
 *  •	ConnectionLifeTime=300 - 连接最大生存时间为 300 秒
 *  •	ConnectionReset=true - 重用连接前重置连接状态
 *  超时控制：
 *  •	ConnectionTimeout=10 - 连接超时设为 10 秒（生产环境要求快速失败）
 *  •	DefaultCommandTimeout=30 - 命令执行超时 30 秒
 *  基础配置：
 *  •	CharSet=utf8mb4 - 使用完整的 Unicode 字符集
 *  •	UseAffectedRows=true - 返回实际受影响的行数
 *  •	Pooling=true - 启用连接池
 *
 *  ****************************  Oracle连接字符串 **************************
 *
 *  "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=your_host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=your_service)));User Id=your_username;Password=your_password;Pooling=True;Min Pool Size=10;Max Pool Size=100;Connection Timeout=15;Connection Lifetime=300;Incr Pool Size=5;Decr Pool Size=2;Validate Connection=True;"
 *
 *  基础配置：
 *  •	HOST=your_host - Oracle 服务器地址
 *  •	PORT=1521 - 默认端口号
 *  •	SERVICE_NAME=your_service - 服务名
 *  •	User Id=your_username - 用户名
 *  •	Password=your_password - 密码
 *
 *  连接池优化：
 *  •	Pooling=True - 启用连接池
 *  •	Min Pool Size=10 - 最小连接数
 *  •	Max Pool Size=100 - 最大连接数
 *  •	Incr Pool Size=5 - 增量连接数
 *  •	Decr Pool Size=2 - 递减连接数
 *  •	Connection Lifetime=300 - 连接生命周期（秒）
 *
 *  性能和可靠性：
 *  •	Connection Timeout=15 - 连接超时时间（秒）
 *  •	Validate Connection=True - 验证连接有效性
 *
 *  ****************************  Sqlserver连接字符串 **************************
 *
 *  "Server=your_server,1433;Database=sgr-prod;User Id=sgr_user;Password=your_strong_password;TrustServerCertificate=True;Encrypt=True;MultipleActiveResultSets=True;Application Name=SGR;Connection Timeout=30;Min Pool Size=10;Max Pool Size=100;Pooling=True;Load Balance Timeout=30;Connection Lifetime=300;ConnectRetryCount=3;ConnectRetryInterval=10;"
 *
 *  基础配置：
 *  •	Server=your_server,1433 - 服务器地址和端口
 *  •	Database=sgr-prod - 数据库名
 *  •	User Id=sgr_user - 用户名
 *  •	Password=your_strong_password - 密码
 *  •	Application Name=SGR - 应用程序名称，用于监控和跟踪
 *
 *  安全配置：
 *  •	TrustServerCertificate=True - 信任服务器证书
 *  •	Encrypt=True - 启用加密连接
 *
 *  连接池优化：
 *  •	Pooling=True - 启用连接池
 *  •	Min Pool Size=10 - 最小连接数
 *  •	Max Pool Size=100 - 最大连接数
 *  •	Connection Lifetime=300 - 连接生命周期（秒）
 *  •	Load Balance Timeout=30 - 负载均衡超时
 *
 *  可靠性设置：
 *  •	ConnectRetryCount=3 - 连接重试次数
 *  •	ConnectRetryInterval=10 - 重试间隔（秒）
 *  •	Connection Timeout=30 - 连接超时时间（秒）
 *  •	MultipleActiveResultSets=True - 启用 MARS，支持多结果集
 *
 *
 *  ****************************  PostgreSql连接字符串 **************************
 *
 *  "Host=your_host;Port=5432;Database=sgr-prod;Username=sgr_user;Password=your_strong_password;SSL Mode=Require;Trust Server Certificate=true;Include Error Detail=true;Maximum Pool Size=100;Minimum Pool Size=10;Connection Idle Lifetime=300;Connection Pruning Interval=10;Timeout=30;Command Timeout=30;Keepalive=30;Application Name=SGR;Search Path=public;Pooling=true;"
 *
 *  基础配置：
 *  •   Host=your_host - PostgreSQL服务器地址
 *  •   Port=5432 - 默认端口号
 *  •   Database=sgr-prod - 数据库名
 *  •   Username=sgr_user - 用户名
 *  •   Password=your_strong_password - 密码
 *  •   Search Path=public - 默认模式
 *  •   Application Name=SGR - 应用程序名称，用于监控
 *
 *  安全配置：
 *  •   SSL Mode=Require - 强制使用SSL连接
 *  •   Trust Server Certificate=true - 信任服务器证书
 *  •   Include Error Detail=true - 包含详细错误信息
 *
 *  连接池优化：
 *  •   Pooling=true - 启用连接池
 *  •   Maximum Pool Size=100 - 最大连接数
 *  •   Minimum Pool Size=10 - 最小连接数
 *  •   Connection Idle Lifetime=300 - 空闲连接超时（秒）
 *  •   Connection Pruning Interval=10 - 清理空闲连接的间隔（秒）
 *
 *  超时和保活设置：
 *  •   Timeout=30 - 连接超时（秒）
 *  •   Command Timeout=30 - 命令超时（秒）
 *  •   Keepalive=30 - 保活间隔（秒）
 *
 *
 **************************************************************/

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Sgr.Utilities;
using System.Data;
using System.Data.Common;

namespace Sgr.Data
{
    /// <summary>
    /// 默认数据库连接字符串提供者
    /// </summary>
    public class DefaultDataConnectionStringProvider : IDataConnectionStringProvider
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<DefaultDataConnectionStringProvider>? _logger;

        /// <summary>
        /// 连接最大生存时间（秒）
        /// </summary>
        private readonly int _connectionLifetime = 300;

        /// <summary>
        /// 连接池最小连接数
        /// </summary>
        private readonly int _minPoolSize = 10;

        /// <summary>
        /// 连接池最大连接数
        /// </summary>
        private readonly int _maxPoolSize = 100;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration">配置接口</param>
        /// <param name="logger">日志器（可注入）</param>
        public DefaultDataConnectionStringProvider(IConfiguration configuration, ILogger<DefaultDataConnectionStringProvider>? logger = null)
        {
            _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
            _logger = logger;
        }

        /// <summary>
        /// 加载数据库连接字符串配置
        /// </summary>
        /// <returns>数据连接字符串集合</returns>
        public IEnumerable<DataConnectionString> LoadDataConnectionStrings()
        {
            var section = _configuration.GetSection("Sgr:DataConnectionStrings");
            if (!section.Exists())
            {
                // 配置缺失时返回空集合（而不是抛出异常）
                _logger?.LogWarning("No database connections configured (Sgr:DataConnectionStrings section missing).");
                yield break;
            }

            var children = section.GetChildren().ToList();
            if (children.Count == 0)
            {
                _logger?.LogWarning("No database connections configured (Sgr:DataConnectionStrings empty).");
                yield break;
            }

            foreach (var child in children)
            {
                // 读取基本字段
                string name = (child.GetValue<string>("Name") ?? child.Key ?? string.Empty).Trim();
                if (string.IsNullOrWhiteSpace(name))
                {
                    _logger?.LogWarning("A DataConnectionString entry missing Name, skip.");
                    continue;
                }

                // DbProvider 支持字符串或数值
                var dbProviderStr = (child.GetValue<string>("DbProvider") ?? string.Empty).Trim();
                if (!Enum.TryParse<DatabaseType>(dbProviderStr, true, out var dbProvider))
                {
                    // 尝试按枚举数值
                    if (!int.TryParse(dbProviderStr, out var enumVal) || !Enum.IsDefined(typeof(DatabaseType), enumVal))
                    {
                        _logger?.LogWarning("Invalid DbProvider for connection '{Name}': {Value}", name, dbProviderStr);
                        continue;
                    }
                    dbProvider = (DatabaseType)enumVal;
                }

                // 读取连接字符串
                string connectionString = child.GetValue<string>("ConnectionString") ?? string.Empty;

                // 读取从库（支持数组或单独配置）
                string[] slaves = Array.Empty<string>();
                var slavesSection = child.GetSection("Slaves");
                if (slavesSection.Exists())
                {
                    // 支持 Slaves 下为数组元素
                    var arr = slavesSection.Get<string[]>();
                    if (arr != null && arr.Length > 0)
                        slaves = arr;
                }

                // 特殊处理与优化
                if (dbProvider == DatabaseType.SQLite)
                {
                    if (string.IsNullOrEmpty(connectionString))
                        connectionString = GetDefaultSqliteDatabaseConnectionString();
                    slaves = Array.Empty<string>();
                }
                else
                {
                    connectionString = OptimizeConnectionString(connectionString, dbProvider);

                    if (slaves != null && slaves.Length > 0)
                        slaves = slaves.Select(s => OptimizeConnectionString(s, dbProvider)).ToArray();
                }

                // 使用不可变构造函数创建实例并返回
                yield return new DataConnectionString(name, dbProvider, connectionString, slaves);
            }
        }

        /// <summary>
        /// 获取默认的SQLite数据库连接字符串
        /// </summary>
        public static string GetDefaultSqliteDatabaseConnectionString()
        {
            return $"Data Source={GetDefaultSqliteDatabaseFilePath()};Mode=ReadWriteCreate;Cache=Shared";
        }

        /// <summary>
        /// 获取默认的SQLite数据库文件路径
        /// </summary>
        public static string GetDefaultSqliteDatabaseFilePath()
        {
            //获取并确保存在数据库文件所在目录
            string dbFileDir = System.IO.Path.Combine(LocalFileHelper.GetApplicationDirectory(), "database");
            if (!System.IO.Directory.Exists(dbFileDir))
                System.IO.Directory.CreateDirectory(dbFileDir);

            //获取数据库文件路径
            return System.IO.Path.Combine(dbFileDir, "sgr.db");
        }

        /// <summary>
        /// 优化连接字符串，添加连接池配置
        /// </summary>
        /// <param name="connectionString">原始连接字符串</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns>优化后的连接字符串</returns>
        private string OptimizeConnectionString(string connectionString, DatabaseType dbType)
        {
            if (string.IsNullOrWhiteSpace(connectionString))
                return connectionString;

            try
            {
                var builder = new DbConnectionStringBuilder { ConnectionString = connectionString };

                // 为不同类型的数据库添加连接池参数
                switch (dbType)
                {
                    case DatabaseType.SqlServer:
                        EnsurePoolingParameter(builder, "Pooling", "true");
                        EnsurePoolingParameter(builder, "Min Pool Size", _minPoolSize.ToString());
                        EnsurePoolingParameter(builder, "Max Pool Size", _maxPoolSize.ToString());
                        EnsurePoolingParameter(builder, "Connection Lifetime", _connectionLifetime.ToString());
                        break;

                    case DatabaseType.MySql:
                        EnsurePoolingParameter(builder, "Pooling", "true");
                        EnsurePoolingParameter(builder, "MinimumPoolSize", _minPoolSize.ToString());
                        EnsurePoolingParameter(builder, "MaximumPoolSize", _maxPoolSize.ToString());
                        EnsurePoolingParameter(builder, "ConnectionLifeTime", _connectionLifetime.ToString());
                        break;

                    case DatabaseType.PostgreSql:
                        EnsurePoolingParameter(builder, "Pooling", "true");
                        EnsurePoolingParameter(builder, "Minimum Pool Size", _minPoolSize.ToString());
                        EnsurePoolingParameter(builder, "Maximum Pool Size", _maxPoolSize.ToString());
                        EnsurePoolingParameter(builder, "Connection Lifetime", _connectionLifetime.ToString());
                        break;

                    case DatabaseType.SQLite:
                        EnsurePoolingParameter(builder, "Pooling", "true");
                        EnsurePoolingParameter(builder, "Max Pool Size", _maxPoolSize.ToString());
                        break;
                }

                return builder.ConnectionString;
            }
            catch
            {
                // 如果解析连接字符串失败，返回原始字符串
                return connectionString;
            }
        }

        /// <summary>
        /// 确保连接字符串中包含指定的连接池参数
        /// </summary>
        /// <param name="builder">连接字符串构建器</param>
        /// <param name="key">参数键名</param>
        /// <param name="value">参数值</param>
        private static void EnsurePoolingParameter(DbConnectionStringBuilder builder, string key, string value)
        {
            if (!builder.ContainsKey(key))
                builder[key] = value;
        }
    }
}